#ifndef BIDK_PLUGIN_SUPPORT_H
#define BIDK_PLUGIN_SUPPORT_H

#ifdef HAVA_CONFIG_H
#include "config.h"
#endif

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include "bidk_compiler.h"
#include "signal.h"
BIDK_BEGIN_C_DECLS

// MAX_BASIC_BLOCK_SIZE should be a power of 2
#define MAX_BASIC_BLOCK_SIZE 64

typedef struct bidk_thread_s bidk_thread;

typedef enum {bidk__cc__bb, bidk__cc__trace, bidk__cc_trace_entry} cc_type;

typedef enum {bidk_vm_op_map, bidk_vm_op_unmap, bidk_vm_op_remap, bidk_vm_op_prot, bidk_vm_op_shmdt} bidk_vm_op_t;

typedef enum {
    bidk_cond_eq,
    bidk_cond_ne,
    bidk_cond_cs,
    bidk_cond_cc,
    bidk_cond_mi,
    bidk_cond_pl,
    bidk_cond_vs,
    bidk_cond_vc,
    bidk_cond_hi,
    bidk_cond_ls,
    bidk_cond_ge,
    bidk_cond_lt,
    bidk_cond_gt,
    bidk_cond_le,
    bidk_cond_al,
    bidk_cond_alt,
} bidk_cond;

typedef enum {
    bidk_pred_pattern_pow2,
    bidk_pred_pattern_vl1,
    bidk_pred_pattern_vl2,
    bidk_pred_pattern_vl3,
    bidk_pred_pattern_vl4,
    bidk_pred_pattern_vl5,
    bidk_pred_pattern_vl6,
    bidk_pred_pattern_vl7,
    bidk_pred_pattern_vl8,
    bidk_pred_pattern_vl16,
    bidk_pred_pattern_vl32,
    bidk_pred_pattern_vl64,
    bidk_pred_pattern_vl128,
    bidk_pred_pattern_vl256,
    bidk_pred_pattern_mul5 = 29,
    bidk_pred_pattern_mul3 = 30,
    bidk_pred_pattern_all = 31,
} bidk_pred_pattern;

struct bidk_code_ctx
{
    cc_type fragment_type;
    int fragment_id;

    void* read_address;
    int inst;
    bidk_cond cond;

    void* write_p;
    void* data_p;

    void* bb_end;

    uint32_t pushed_regs;
    uint32_t available_regs;
    int plugin_pushed_reg_count;

    const char* func_name;
    void* func_data;

    bool replace;
    bool* stop;
};

struct bidk_syscall_ctx
{
    uintptr_t number;
    uintptr_t* regs;
    bool replace;
    uintptr_t ret;
};

struct bidk_vm_ctx
{
    bidk_vm_op_t op;
    uintptr_t addr;
    size_t size;
    unsigned prot;
    unsigned flags;
    int filedes;
    off_t off;
    uintptr_t prev_addr;
    size_t prev_size;
};

struct bidk_sym_parse_ctx
{
    const char* name;
    void* addr;
    unsigned size;
};

struct bidk_dl_loader_ctx
{
    const char* name;
    int fd;
};

struct bidk_signal_ctx
{
    int signal_idx;
    siginfo_t* info;
    ucontext_t* context;
};

typedef enum {
    bidk_cb_plugin_reg,
    bidk_cb_pre_inst,
    bidk_cb_post_inst,
    bidk_cb_pre_bb,
    bidk_cb_post_bb,
    bidk_cb_pre_fragment,
    bidk_cb_post_fragment,
    bidk_cb_pre_syscall,
    bidk_cb_post_syscall,
    bidk_cb_pre_thread,
    bidk_cb_post_thread,
    bidk_cb_pre_fn,
    bidk_cb_post_fn,
    bidk_cb_exit,
    bidk_cb_vm_op,
    bidk_cb_sym_parse,
    bidk_cb_loader,
    bidk_cb_plugin_priv,
    bidk_cb_flush_code_cache,
    bidk_cb_pre_dispatcher,
    bidk_cb_post_dispatcher,
    bidk_cb_signal,
    bidk_cb_max_idx,
} bidk_cb_idx;

typedef unsigned bidk_return_code;

typedef struct bidk_context_s
{
    bidk_thread* thread_data;
    bidk_cb_idx event_type;
    int plugin_id;
    bool before_cb;

    union 
    {
        bidk_return_code* retcode;
        struct bidk_code_ctx code;
        struct bidk_syscall_ctx syscall;
        struct bidk_vm_ctx vm;
        struct bidk_sym_parse_ctx sym_parse;
        struct bidk_dl_loader_ctx dl_loader;
        struct bidk_plugin_priv_ctx plugin_priv;
        struct bidk_signal_cxt signal;
    };
} bidk_context;

typedef int (*bidk_callback)(bidk_context* ctx);

typedef enum {
    bidk_branch_none = bidk_bit(0);
    bidk_branch_direct = bidk_bit(1);
    bidk_branch_indirect = bidk_bit(2);
    bidk_branch_return = bidk_bit(3);
    bidk_branch_cond = bidk_bit(4);
    bidk_branch_cond_psr = bidk_bit(5);
    bidk_branch_cbz = bidk_bit(6);
    bidk_branch_tbz = bidk_bit(7);
    bidk_branch_call = bidk_bit(8);
} bidk_branch_type;

/* Stack frame */
typedef struct bidk_stack_frame
{
    struct bidk_stack_frame* prev;
    uintptr_t lr;
} bidk_stack_frame_t;

typedef struct {
    unsigned long flags;
    void* child_stack；
    pid_t* ptid;
    uintptr_t tls;
    pid_t* ctid;
} bidk_sys_clone_args;

/* Public functions */
bidk_context* bidk_register_plugin(void);

int bidk_register_pre_inst_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_post_inst_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_pre_basic_block_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_post_basic_block_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_pre_fragment_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_post_fragment_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_pre_syscall_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_post_syscall_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_pre_thread_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_post_thread_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_pre_func_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_post_func_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_exit_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_vm_op_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_sym_parse_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_dl_loader_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_plugin_priv_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_flush_code_cache_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_pre_dispatcher_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_post_bb_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_pre_func_log_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_post_func_log_cb(bidk_context* ctx, bidk_callback cb);
int bidk_register_signal_cb(bidk_context* ctx, int signal_idx, bidk_callback cb);
int bidk_register_signals_cb(bidk_context* ctx, bidk_callback cb);

/* Access plugin data */
int bidk_set_plugin_data(bidk_context* ctx, void* data);
void* bidk_get_plugin_data(bidk_context* ctx);\
int bidk_set_thread_plugin_data(bidk_context* ctx, void* data);
int bidk_get_thread_plugin_data(bidk_context* ctx);
bidk_thread* bidk_get_next_thread(bidk_context* ctx);
bidk_sys_clone_args* bidk_get_clone_args(bidk_context* ctx);
const void* bidk_get_thread_stack(bidk_context* ctx);

/* Syscalls */
int bidk_syscall_get_no(bidk_context* ctx);
void* bidk_syscall_get_args(bidk_context* ctx);
int bidk_syscall_bypass(bidk_context* ctx);
uintptr_t bidk_syscall_get_return(bidk_context* ctx);
int bidk_syscall_set_return(bidk_context* ctx, uintptr_t ret);

/* VM-callback specific */
bidk_vm_op_t bidk_get_vm_op(bidk_context* ctx);
uintptr_t bidk_get_vm_addr(bidk_context* ctx);
size_t bidk_get_vm_size(bidk_context* ctx);
int bidk_get_vm_prot(bidk_context* ctx);
int bidk_get_vm_flags(bidk_context* ctx);
int bidk_get_vm_filedes(bidk_context* ctx);
int bidk_get_vm_off(bidk_context* ctx);

/* Other*/
int bidk_get_inst(bidk_context* ctx);
int bidk_get_fragment_id(bidk_context* ctx);
cc_type bidk_get_fragment_type(bidk_context* ctx);
int bidk_get_inst_len(bidk_context* ctx);
void* bidk_get_source_addr(bidk_context* ctx);
int bidk_set_source_addr(bidk_context* ctx, void* source_addr);
void* bidk_get_cc_addr(bidk_context* ctx);
void* bidk_set_cc_addr(bidk_context* ctx, void* addr);
pid_t bidk_get_thread_id(bidk_context* ctx);
bool bidk_is_cond(bidk_context* ctx);
bidk_cond bidk_get_cond(bidk_context* ctx);
bidk_cond bidk_get_inverted_cond(bidk_context* ctx, bidk_cond cond);
void bidk_replace_inst(bidk_context* ctx);
bool bidk_is_load(bidk_context* ctx);
bool bidk_is_store(bidk_context* ctx);
bool bidk_is_load_or_store(bidk_context* ctx);
int bidk_get_ld_st_size(bidk_context* ctx);
int bidk_add_identity_mapping(bidk_context* ctx);
const char* bidk_get_cb_function_name(bidk_context* ctx);
int char* bidk_stop_scan(bidk_context* ctx);
int bidk_reserve_cc_space(bidk_context* ctx, size_t size);
int bidk_is_last_instruction_in_bb(bidk_context* ctx);
bool bidk_disable_post_fn_callbacks(bool desired);

bidk_branch_type bidk_get_branch_type(bidk_context* ctx);
void bidk_flush_code_cache(bidk_thread* thread_data);

/* Symbol-related functions */
int bidk_get_symbol_info_by_addr(uintptr_t addr, char** sym_name, void** start_addr, char** filename);
typedef int (*bidk_stack_frame_handler)(void* data, void* addr, chr* sym_name, void* symbol_start_addr,
                                        char* filename);
int bidk_get_backtrace(bidk_stack_frame_t* fp, bidk_stack_frame_handler handler, void* ptr);

int bidk_run(int argc, char** argv, char** envp, int optarg);

BIDK_END_C_DECLS

#endif













